import { any } from "../../../core/logic";
import { Engine } from "../../../Engine";
import { Pass } from "../Pass";
import { Effect, EffectType } from "./Effect";
import { fxaa_frag, fxaa_frag_multisampled } from "./shaders/fxaa_frag";
import { fxaa_vert, fxaa_vert_multisampled } from "./shaders/fxaa_vert";
/**
 * 后处理快色抗锯齿。
 * @class
 * @memberof w.effects
 */
class Fxaa extends Effect {
  /**
   * @constructor
   * @param {*} opts
   * @param {Number} opts.FXAA_REDUCE_MIN 缺省值1.0/128.0
   * 这个参数调整了锯齿减少量的乘数。较大的值会使FXAA减少更多的锯齿，但可能会导致一些细节的模糊。
   * 较小的值则会减少细节的模糊，但也可能对锯齿的减少效果减弱。
   * 你可以根据需要调整这个值以达到最佳效果。
   * @param {Number} opts.FXAA_REDUCE_MUL 缺省值1.0/8.0
   * 这个参数控制着带宽的最大跨度。较大的值可以处理更大的边缘锯齿，但也可能导致一些细节的模糊。
   * 较小的值则可以保留更多细节，但可能无法充分减少边缘锯齿。
   * 你可以根据需要进行调整，以平衡锯齿减少和细节保留之间的效果。
   * @param {Number} opts.FXAA_SPAN_MAX 缺省值8.0
   * 这个参数控制着对锯齿的最小减少量。较低的值会使FXAA更积极地减少锯齿，但也可能导致一些细节的损失。
   * 较高的值则会减少细节损失，但可能对锯齿的减少效果不够明显。
   * 你可以尝试不同的值来找到最佳的平衡点。
   */
  constructor(opts = {}) {
    super(opts);
    this.type = EffectType.Fxaa;
    this.FXAA_REDUCE_MIN = any(opts.FXAA_REDUCE_MIN, 1.0 / 128.0);
    this.FXAA_REDUCE_MUL = any(opts.FXAA_REDUCE_MUL, 1.0 / 8.0);
    this.FXAA_SPAN_MAX = any(opts.FXAA_SPAN_MAX, 8.0);
    this.sampler = Engine.instance.device.createSampler({
      magFilter: "linear", //nearest linear
      minFilter: "linear",
    });
    this.initBuffer();
    // this.initPipeLine();
  }
  initBuffer() {
    this.resolutionBuffer = Engine.instance.device.createBuffer({
      label: "resolution",
      size: 8,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });
  }
  initPipeLine() {

    if (!this.pipeLine) {

      this.pipeLine = Engine.instance.device.createRenderPipeline({
        label: "fxaa Pipline",
        layout: `auto`,
        vertex: {
          module: Engine.instance.device.createShaderModule({
            code: fxaa_vert,
          }),
          entryPoint: "main",
        },
        fragment: {
          module: Engine.instance.device.createShaderModule({
            code: fxaa_frag,
          }),
          constants: {
            FXAA_REDUCE_MIN: this.FXAA_REDUCE_MIN,
            FXAA_REDUCE_MUL: this.FXAA_REDUCE_MUL,
            FXAA_SPAN_MAX: this.FXAA_SPAN_MAX,
          },
          entryPoint: "main",
          targets: [
            {
              format: Engine.instance.format,
            },
          ],
        },
        primitive: {
          topology: "triangle-list",
          cullMode: "back",
          frontFace: "ccw",
        },
        multisample: {
          count: 1,
        },
      });
    }

  }
  initBindGroup(texture) {
    if (this.group && this.group.id != texture.id) {
      this.group = null;
    }
    if (!this.group) {
      this.group = Engine.instance.device.createBindGroup({
        label: "fxaa group",
        layout: this.pipeLine.getBindGroupLayout(0),
        entries: [
          {
            binding: 0,
            resource: {
              buffer: this.resolutionBuffer,
              size: 8,
            },
          },
          {
            binding: 1,
            resource: this.sampler,
          },
          {
            binding: 2,
            resource: texture.view,
          },
        ],
      });
      this.group.id = texture.id;
    }
    return this.group;
  }
  onRender(commandEncoder) {
    //执行帧渲染
    const pass = super.onRender(commandEncoder);
    this.initPipeLine()
    const texture = Pass.instance().readAttach;
    const renderPass = commandEncoder.beginRenderPass(pass);
    renderPass.setPipeline(this.pipeLine);
    let bindGroup = this.initBindGroup(texture);
    // set uniformGroup
    Engine.instance.queue.writeBuffer(
      this.resolutionBuffer,
      0,
      new Float32Array(Engine.instance.size)
    );

    renderPass.setBindGroup(0, bindGroup);
    renderPass.draw(3);
    renderPass.end();
  }
  getDescript() {
    if (this._descript) {
      if (
        this._descript.size[0] !== Engine.instance.size[0] ||
        this._descript.size[1] !== Engine.instance.size[1]
      ) {
        this._descript = null;
      } else {
        this._descript.colorAttachments[0].view =
          Pass.instance().writeAttach.view;
      }
    }
    if (!this._descript) {
      this._descript = {
        colorAttachments: [
          {
            view: Pass.instance().writeAttach.view,
            loadOp: "clear",
            clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
            storeOp: "store",
          },
        ],
      };
      this._descript.size = Engine.instance.size;
    }
    return this._descript;
  }
}
export { Fxaa };
